import os
import shutil
import time
import configparser
import logging
from datetime import datetime
import platform
import subprocess
import re
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

'''
这个程序实现了以下核心功能：
跨平台支持：能够在 Windows、Linux 和 macOS 系统上运行
参数化配置：所有关键参数均可通过配置文件设置
实时监控：定时检查 USB 设备插入事件
自动拷贝：发现新的 USB 存储设备后自动拷贝指定类型的文件
日志记录：详细记录程序运行状态和操作过程

使用方法
首先安装必要的依赖库：pip install watchdog
运行程序，它会自动创建默认配置文件usb_monitor_config.ini
根据需要修改配置文件中的参数
重新启动程序使配置生效

配置文件说明
配置文件usb_monitor_config.ini包含以下可配置参数：
[monitor]部分：
check_interval：检查 USB 设备的时间间隔（秒）
log_file：日志文件存储路径
[copy]部分：
target_directory：文件拷贝的目标目录
file_formats：需要拷贝的文件格式，用逗号分隔，*表示所有格式
exclude_directories：需要排除的目录，用逗号分隔
程序运行时会在后台持续监控 USB 设备，当有新的 USB 存储设备插入时，会自动按照配置进行文件拷贝操作。所有操作都会记录在日志文件中，便于后续查看。


'''
# 配置日志记录
def setup_logging(log_file):
    """配置日志系统，记录程序运行状态和错误信息"""
    # 创建日志目录（如果不存在）
    log_dir = os.path.dirname(log_file)
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    # 配置日志格式和级别
    logging.basicConfig(
        filename=log_file,
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )

    # 同时在控制台输出日志
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)


class USBMonitor:
    """USB设备监控类，负责检测USB设备的插入和移除"""

    def __init__(self, config):
        """初始化USB监控器

        Args:
            config: 配置解析器对象，包含程序所有配置参数
        """
        # 加载配置参数
        self.check_interval = int(config.get('monitor', 'check_interval'))  # 检查间隔（秒）
        self.target_dir = config.get('copy', 'target_directory')  # 目标存储目录
        self.file_formats = config.get('copy', 'file_formats').split(',')  # 需要拷贝的文件格式
        self.exclude_dirs = config.get('copy', 'exclude_directories').split(',')  # 排除的目录

        # 确保目标目录存在
        if not os.path.exists(self.target_dir):
            os.makedirs(self.target_dir)

        # 记录已检测到的USB设备，避免重复处理
        self.known_devices = set()

        # 记录系统类型（Windows/Linux/macOS）
        self.os_type = platform.system()

        logging.info(f"USB监控器初始化完成，检查间隔: {self.check_interval}秒")
        logging.info(f"目标存储目录: {self.target_dir}")
        logging.info(f"需要拷贝的文件格式: {', '.join(self.file_formats)}")

    def get_usb_devices(self):
        """获取当前连接的USB存储设备列表

        Returns:
            list: 包含USB设备信息的字典列表，每个字典包含设备路径和名称
        """
        devices = []

        if self.os_type == "Windows":
            # Windows系统通过wmic命令获取USB存储设备
            try:
                # 执行命令获取所有磁盘驱动器信息
                result = subprocess.check_output(
                    'wmic logicaldisk where drivetype=2 get deviceid, volumename',
                    shell=True,
                    encoding='gbk'  # 使用gbk编码处理中文
                )

                # 解析命令输出
                lines = [line.strip() for line in result.split('\n') if line.strip()]
                # 跳过标题行
                for line in lines[1:]:
                    parts = re.split(r'\s+', line, 1)  # 分割设备ID和卷名
                    if len(parts) >= 1:
                        device_id = parts[0]
                        volume_name = parts[1] if len(parts) > 1 else "未知设备"
                        devices.append({
                            'path': device_id,
                            'name': volume_name
                        })
            except Exception as e:
                logging.error(f"Windows系统获取USB设备失败: {str(e)}")

        elif self.os_type == "Linux":
            # Linux系统通过lsblk命令获取USB存储设备
            try:
                # 执行命令获取USB存储设备信息
                result = subprocess.check_output(
                    "lsblk -o NAME,MOUNTPOINT,TYPE,LABEL -n | grep 'disk' | grep -v 'loop'",
                    shell=True,
                    encoding='utf-8'
                )

                # 解析命令输出
                for line in result.split('\n'):
                    if line.strip():
                        parts = re.split(r'\s+', line.strip())
                        if len(parts) >= 3 and parts[1] and parts[1] != '':
                            devices.append({
                                'path': parts[1],
                                'name': parts[3] if len(parts) > 3 else parts[0]
                            })
            except Exception as e:
                logging.error(f"Linux系统获取USB设备失败: {str(e)}")

        elif self.os_type == "Darwin":  # macOS
            # macOS系统通过diskutil命令获取USB存储设备
            try:
                # 执行命令获取外部磁盘信息
                result = subprocess.check_output(
                    "diskutil list external | grep -E '^/dev/'",
                    shell=True,
                    encoding='utf-8'
                )

                # 解析命令输出
                for line in result.split('\n'):
                    if line.strip():
                        parts = re.split(r'\s+', line.strip(), 2)
                        if len(parts) >= 2:
                            # 获取挂载点
                            mount_result = subprocess.check_output(
                                f"diskutil info {parts[0]} | grep 'Mount Point' | awk -F: '{{print $2}}' | sed 's/^ //'",
                                shell=True,
                                encoding='utf-8'
                            ).strip()

                            if mount_result and mount_result != "Not mounted":
                                # 获取卷标
                                label_result = subprocess.check_output(
                                    f"diskutil info {parts[0]} | grep 'Volume Name' | awk -F: '{{print $2}}' | sed 's/^ //'",
                                    shell=True,
                                    encoding='utf-8'
                                ).strip()

                                devices.append({
                                    'path': mount_result,
                                    'name': label_result if label_result else parts[0]
                                })
            except Exception as e:
                logging.error(f"macOS系统获取USB设备失败: {str(e)}")

        else:
            logging.warning(f"不支持的操作系统: {self.os_type}")

        return devices

    def is_new_device(self, device):
        """判断设备是否为新插入的设备

        Args:
            device: 设备信息字典

        Returns:
            bool: 如果是新设备返回True，否则返回False
        """
        return device['path'] not in self.known_devices

    def add_known_device(self, device):
        """将设备添加到已知设备列表

        Args:
            device: 设备信息字典
        """
        self.known_devices.add(device['path'])

    def process_new_device(self, device):
        """处理新插入的USB设备，拷贝指定文件

        Args:
            device: 设备信息字典
        """
        device_path = device['path']
        device_name = device['name']

        logging.info(f"检测到新的USB设备: {device_name}，路径: {device_path}")

        try:
            # 创建以设备名和当前时间命名的目录，用于存储拷贝的文件
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            dest_dir = os.path.join(self.target_dir, f"{device_name}_{timestamp}")
            os.makedirs(dest_dir, exist_ok=True)

            logging.info(f"开始拷贝文件到: {dest_dir}")

            # 递归遍历USB设备中的所有文件
            file_counter = 0
            for root, dirs, files in os.walk(device_path):
                # 过滤掉需要排除的目录
                dirs[:] = [d for d in dirs if d not in self.exclude_dirs]

                for file in files:
                    # 检查文件扩展名是否在需要拷贝的列表中
                    file_ext = os.path.splitext(file)[1].lower().lstrip('.')
                    if file_ext in self.file_formats or '*' in self.file_formats:
                        src_path = os.path.join(root, file)
                        # 构建目标路径，保持原有的目录结构
                        rel_path = os.path.relpath(src_path, device_path)
                        dest_path = os.path.join(dest_dir, rel_path)

                        # 创建目标目录
                        os.makedirs(os.path.dirname(dest_path), exist_ok=True)

                        try:
                            # 拷贝文件
                            shutil.copy2(src_path, dest_path)  # 使用copy2保留文件元数据
                            file_counter += 1
                            logging.debug(f"已拷贝: {src_path} -> {dest_path}")

                            # 每拷贝10个文件输出一次进度
                            if file_counter % 10 == 0:
                                logging.info(f"已拷贝 {file_counter} 个文件...")
                        except Exception as e:
                            logging.error(f"拷贝文件 {src_path} 失败: {str(e)}")

            logging.info(f"文件拷贝完成，共拷贝 {file_counter} 个文件到 {dest_dir}")

        except Exception as e:
            logging.error(f"处理USB设备 {device_name} 时出错: {str(e)}")

    def start_monitoring(self):
        """开始监控USB设备插入事件"""
        logging.info("开始监控USB设备...")

        try:
            while True:
                # 获取当前连接的USB设备
                current_devices = self.get_usb_devices()

                # 检查新设备
                for device in current_devices:
                    if self.is_new_device(device):
                        self.add_known_device(device)
                        self.process_new_device(device)

                # 等待指定的时间间隔后再次检查
                time.sleep(self.check_interval)

        except KeyboardInterrupt:
            logging.info("用户中断，程序退出")
        except Exception as e:
            logging.error(f"监控过程中发生错误: {str(e)}")


def main():
    """主函数，程序入口点"""
    # 读取配置文件
    config = configparser.ConfigParser()
    config_path = 'usb_monitor_config.ini'

    # 检查配置文件是否存在，如果不存在则创建默认配置
    if not os.path.exists(config_path):
        # 创建默认配置
        config['monitor'] = {
            'check_interval': '5',  # 检查间隔（秒）
            'log_file': 'logs/usb_monitor.log'  # 日志文件路径
        }
        config['copy'] = {
            'target_directory': 'usb_files',  # 目标存储目录
            'file_formats': 'jpg,jpeg,png,gif,bmp,mp4,avi,mov,mp3,doc,docx,xls,xlsx,pdf,zip,rar,txt',  # 需要拷贝的文件格式
            'exclude_directories': 'System Volume Information,Recycler,$RECYCLE.BIN'  # 排除的系统目录
        }

        # 保存默认配置到文件
        with open(config_path, 'w') as f:
            config.write(f)

        logging.warning(f"配置文件不存在，已创建默认配置文件: {config_path}")

    # 读取配置文件
    config.read(config_path)

    # 配置日志
    log_file = config.get('monitor', 'log_file')
    setup_logging(log_file)

    # 创建并启动USB监控器
    monitor = USBMonitor(config)
    monitor.start_monitoring()


if __name__ == "__main__":
    main()
